#include "logtxt.hpp"
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <cstring>
#include <netinet/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <strings.h>
#include <functional>
#include <unordered_map>

typedef function<string(const string &, const string &, uint16_t)> func_t;
Log log;
using namespace std;
enum
{
    SOCKRT_ERR = 1,
    BIND_ERR
};
class udpServer
{
public:
    udpServer(const uint16_t &port, string ip = "0.0.0.0") : port_(port), ip_(ip), sockfd_(0), is_runing_(false)
    {
    }
    void Init()
    {
        // 创建套接字
        sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);
        if (sockfd_ < 0)
        {
            log(fatal, "socket fail,error:%d,strerrno:%s\n", errno, strerror(errno));
            exit(SOCKRT_ERR);
        }

        log(Info, "socket success,error:%d,strerrno:%s", errno, strerror(errno));

        // 选择套接字域
        struct sockaddr_in load;
        bzero(&load, sizeof(load));
        load.sin_family = AF_INET; // ipv4
        // load.sin_addr.s_addr = inet_addr(ip_.c_str()); // 将.分十进制字符串转换为长整型
        load.sin_addr.s_addr = INADDR_ANY; // 云服务器使用0
        load.sin_port = htons(port_);

        socklen_t size = sizeof(load);
        // 绑定
        int n = bind(sockfd_, (const sockaddr *)&load, size);
        if (n < 0)
        {
            log(fatal, "bind fail,error:%d,strerrno:%s", errno, strerror(errno));
            exit(BIND_ERR);
        }
        log(Info, "bind success,error:%d,strerrno:%s", errno, strerror(errno));
    }
    void checkexist(struct sockaddr_in &client, const string &clientip, uint16_t clientport)
    {
        auto iter = Online_list.find(clientip);
        if (iter == Online_list.end())
        {
            Online_list.insert({clientip, client});
            std::cout << "[" << clientip << ":" << clientport << "] add to online user." << std::endl;
        }
    }
    void Broadcast(const string &clientip, uint16_t clientport, const string &info)
    {

        for (const auto &x : Online_list)
        {
            string message;
            message += "[";
            message += clientip;
            message += ":";
            message += clientport;
            message += "]#";
            message += info;
            socklen_t len = sizeof(x.second);
            sendto(sockfd_, message.c_str(), message.size(), 0, (const struct sockaddr *)(&x.second), len);
        }
    }
    void Run(func_t func)
    {
        is_runing_ = true;

        while (is_runing_)
        {
            char buffer[1024];
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            ssize_t s = recvfrom(sockfd_, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&client, &len);
            if (s < 0)
            {
                log(Warning, "recvfrom error, errno: %d, err string: %s", errno, strerror(errno));
                continue;
            }
            buffer[s] = 0;
            std::string info = buffer;

            string clientip = inet_ntoa(client.sin_addr);
            uint16_t clientport = ntohs(client.sin_port);

            checkexist(client, clientip, clientport); // 检查客户端是否在线

            Broadcast(clientip, clientport, info); // 广播消息给所有在线客户
            // std::string echo_string = "slever@" + info;
            // cout << echo_string << endl;
            // std::string echo_string = func(info, clientip, clientport);
            // sendto(sockfd_, echo_string.c_str(), echo_string.size(), 0, (const struct sockaddr *)&client, len);
        }
    }
    ~udpServer()
    {
        if (sockfd_ > 0)
        {
            close(sockfd_);
        }
    }

private:
    uint16_t port_;
    string ip_;
    int sockfd_;
    bool is_runing_;
    unordered_map<string, struct sockaddr_in> Online_list;
};